package com.study.talk.netty;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;

import com.study.talk.netty.message.AndroidChannelInitializer;
import com.study.common.event.Constants;
import com.study.common.EventBusUtil;
import com.study.common.event.OtherMessage;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class AndroidNetty implements Netty {

    public static String TAG = "Android Netty";
    private Handler nettyHandler;
    private HandlerThread nettyHandlerThread;
    private Handler mainHandler;
    private Bootstrap bootstrap;
    private ChannelInitializer<SocketChannel> initializer;
    private ChannelFuture nettyChannelFuture;
    private static final int NETTY_INIT = 0x01;
    private static final int NETTY_CONNECT = 0x02;
    private static final int NETTY_SEND_MESSAGE = 0x03;
    private static final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
    private static Channel channel;

    private OnConnectLister onConnectLister;

    private OnSendMessageListener onSendMessageListener;

    private final String ip;
    private final Integer port;

    public AndroidNetty(String ip,Integer port,ChannelInitializer<SocketChannel> initializer) {
        this.ip = ip;
        this.port = port;
        this.initializer = initializer;
        handlerThreadInit();
        handlerInit();
        connect();
    }

    private void handlerThreadInit(){
        mainHandler = new Handler(Looper.getMainLooper());
        nettyHandlerThread = new HandlerThread(NettyClient.class.getName());
        nettyHandlerThread.start();
        nettyHandler = new Handler(nettyHandlerThread.getLooper()){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                switch (msg.what){
                    case NETTY_INIT -> handlerInit();
                    case NETTY_CONNECT -> connect();
                    case NETTY_SEND_MESSAGE -> handlerSendMessage(msg.obj);
                }
            }
        };
    }

    @Override
    public void connect() {
        if (isConnected()) return;
        connect(5);
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            if (channel == null || !channel.isActive() || !channel.isOpen() || !channel.isWritable()) {
                Log.e(TAG, "connect: 尝试重新连接");
                connect(1);
            }
        }, 30, 30, TimeUnit.SECONDS);
    }


    private void handlerInit() {
        NioEventLoopGroup group = new NioEventLoopGroup();
        initializer = new AndroidChannelInitializer();
        bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .remoteAddress(new InetSocketAddress(ip, port))
                .handler(initializer)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.SO_KEEPALIVE, true);
    }

    private void connect(final int retry) {
        try {
            nettyChannelFuture = bootstrap.connect(ip, port).addListener(future -> {
                if (future.isSuccess()) {
                    Log.e(TAG, "connect: 启动连接成功");
                    if (channel != null) {
                        channel.close();
                    }
                    channel = ((ChannelFuture) future).channel();
                    if (null != onConnectLister){
                        if (future.isSuccess()) {
                            onConnectLister.success();
                        } else {
                            onConnectLister.failed();
                        }
                    }
                } else if (retry == 0) {
                    Log.e(TAG, "connect: 不再重试连接");
                    scheduledExecutorService.shutdownNow();
                    EventBusUtil.sendEvent(new OtherMessage(Constants.CONNECTION_ERROR));
                } else {
                    int sleepSecond = 1 << retry;
                    Thread.sleep(sleepSecond);
                    connect(retry - 1);
                }
            });
        }catch (Exception e){
            e.printStackTrace();
            if (null != onConnectLister){
                onConnectLister.error();
            }
        }
    }

    private void handlerSendMessage(Object msg){
        if (isOpen()){
            nettyChannelFuture.channel().writeAndFlush(msg).addListener(future -> {
                boolean success = future.isSuccess();
                if (success){
                    Log.d(TAG, "handlerSendMessage: "+msg);
                }else {
                    Log.d(TAG, "handlerSendMessage: "+"failed");
                }
                if (onSendMessageListener != null){
                    mainHandler.post(()->onSendMessageListener.onSendMessage(msg,success));
                }
            });
        }
    }

    @Override
    public void reconnect(long time) {
        close();
        nettyHandler.sendEmptyMessageDelayed(NETTY_CONNECT,time);
    }

    @Override
    public void sendMessage(Object msg){
        nettyHandler.obtainMessage(NETTY_SEND_MESSAGE,msg).sendToTarget();
    }

    @Override
    public void setOnConnectListener(OnConnectLister listener) {
        this.onConnectLister = listener;
    }

    @Override
    public void setOnSendMessageListener(OnSendMessageListener listener) {
        this.onSendMessageListener = listener;
    }

    @Override
    public void close() {
        if (isOpen()){
            nettyChannelFuture.channel().close();
            Log.d(TAG, "Netty channel connect closed");
        }
    }

    @Override
    public void disconnect() {
        if (isConnected()){
            nettyChannelFuture.channel().disconnect();
            Log.d(TAG, "Netty channel connect disconnected");
        }
    }

    @Override
    public boolean isConnected() {
        boolean isConnected = nettyChannelFuture != null && nettyChannelFuture.channel().isActive();
        if (!isConnected) Log.w(TAG, "isConnected: Netty Channel is not connect");
        return isConnected;
    }

    @Override
    public boolean isOpen() {
        boolean isOpen = nettyChannelFuture != null && nettyChannelFuture.channel().isOpen();
        if (!isOpen) Log.w(TAG, "isConnected: Netty Channel is not Open");
        return isOpen;
    }

    @Override
    public ChannelFuture getChannelFuture() {
        return nettyChannelFuture;
    }

}
